//
//  BBManipulateView.m
//  BBCoverFlowTest
//
//  Created by ben smith on 3/10/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "BBManipulateView.h"
#import "BBPhotoLayer.h"
#import "BBGestureDecoder.h"
#import "BBTUIOCursor.h"
#import "BBMovieLayer.h"


@implementation BBManipulateView

@synthesize cursors;

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
	[self didStartManipulating];
	[self didEndManipulating];
}

-(NSArray*)observingKeyPaths
{
	return [NSArray arrayWithObjects:@"BBShowShadowsFlag",@"BBShadowOpacity",@"BBButtonVerticalOffset",nil];
}

-(void)setupLayers
{
	for (NSString * keyPath in [self observingKeyPaths]) {
		[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:nil];			
	}
	circle1 = YES;
	if (movingParts == nil) movingParts = [[NSMutableArray alloc] init];
	
//	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(blobEventsNotification:) name:@"BBTUIOCursors" object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registerMoving:) name:@"BBRegisterMoving" object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deregisterMoving:) name:@"BBDeregisterMoving" object:nil];

	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(blobDownEvent:) name:@"BBBlobDownEvent" object:nil];

	
	gestureDecoder = [[BBGestureDecoder alloc] init];
	initialZPos = 0;
	[super setupLayers];
	[[self layer] addSublayer:coverButtonLayer];
}

-(void)setupButtons
{
	[super setupButtons];
	
	[[self layer] addSublayer:coverButtonLayer];
	[[self layer] addSublayer:quitButtonLayer];
	[[self layer] addSublayer:circleLayoutButtonLayer];
	[[self layer] addSublayer:lightBoxButtonLayer];
	
	[buttonsInView addObject:lightBoxButtonLayer];
	[buttonsInView addObject:circleLayoutButtonLayer];
	[buttonsInView addObject:coverButtonLayer];
	[buttonsInView addObject:quitButtonLayer];
}


-(void)addImageFiles:(NSArray*)fileNames;
{
	[super addImageFiles:fileNames];
	[self layoutMess];
}

-(void)registerMoving:(NSNotification*)note
{
	[movingParts addObject:[note object]];
}

-(void)deregisterMoving:(NSNotification*)note
{
	if (movingParts == nil) movingParts = [[NSMutableArray alloc] init];
	[movingParts removeObject:[note object]];
	if ([movingParts count] == 0) [self didEndManipulating];
}

-(void)layoutMess
{
	maxZpos = 3;
	srandom((unsigned)time(0)); 
	float dist = 450;
	[CATransaction begin];
	for (CALayer * layer in [bodyLayer sublayers]) {
		int randomX = random();
		randomX = randomX & 0xff;
		CGFloat xPos = (dist/2.0) - (((CGFloat)randomX/255.0) * dist);
    int randomY = random();
		randomY = randomY & 0xff;
		CGFloat yPos = (dist/2.0) - (((CGFloat)randomY/255.0) * dist);

		int randomAngle = random();
		randomAngle = randomAngle&0xff;
		CGFloat angle = 0.5 - ((CGFloat)randomAngle/255.0) * 1.0;
		CATransform3D shmush =  CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0);
		layer.sublayerTransform = shmush; 	
		
		layer.position = CGPointMake(NSMidX([self bounds]) + xPos, NSMidY([self bounds]) + yPos);

		layer.zPosition = angle;
	}	
	[self handleButtonLayout];
	[CATransaction commit];
//	[self performSelector:@selector(layoutCircle) withObject:nil afterDelay:8.0];
}


-(CALayer*)photoLayer
{	
	CALayer * carrier = [CALayer layer];
	
	CGRect r;
	r.origin = CGPointZero;
	r.size.height = 300;
	r.size.width = 300;
	carrier.name = @"photoLayer";
	[carrier setFrame:r];
	//carrier.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 0.0, 0.5); // yellow
	//carrier.borderWidth = 1.0;	
	
	CGPoint p = carrier.position;
	p.x = NSMidX([self bounds]);
	p.y = NSMidY([self bounds]);
	carrier.position = p;


	BBPhotoLayer * photoLayer = [BBPhotoLayer layer];
	photoLayer.name = @"image";
	photoLayer.backgroundColor = CGColorCreateGenericRGB(0.2, 0.2, 0.2, 1.0);
	[photoLayer setFrame:r];
//	photoLayer.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 0.0, 0.5); // yellow
//	photoLayer.borderWidth = 1.0;	
	photoLayer.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 1.0, 1.0); 
	photoLayer.borderWidth = 3.0;
	photoLayer.shadowColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0);
	

	photoLayer.anchorPoint = CGPointMake(0.5,0.5);
	photoLayer.position = CGPointMake(150, 150);
	[carrier addSublayer:photoLayer];
	photoLayer.opaque = YES;
	carrier.zPosition = initialZPos--;
	
	return carrier;
}



-(void)convertToMovieLayer:(CALayer*)aLayer
{		
	// first remove the photolayer
	QTMovie * movie = [QTMovie movieWithFile:aLayer.name error:nil];

	CALayer * photoLayer = [[aLayer sublayers] objectAtIndex:0];
	[photoLayer removeFromSuperlayer];
	
	NSSize imageSize = [[movie posterImage] size];
	
	CGRect frame = CGRectMake(0, 0, imageSize.width, imageSize.height);
	CGPoint oldLayerPos = aLayer.position;
	
	[aLayer setFrame:frame];
	[aLayer setPosition:oldLayerPos];
	
	// now build up my movie layer
	BBMovieLayer * movielayer = [BBMovieLayer layerWithMovie:movie];
	[movielayer setFrame:frame];
	[movielayer startListening];
	movielayer.name = @"MovieLayer";
	movielayer.position = CGPointMake(frame.size.width/2.0,frame.size.height/2.0);
	movielayer.borderColor = CGColorCreateGenericRGB(1.0, 1.0, 1.0, 1.0); 
	movielayer.borderWidth = 3.0;
	movielayer.shadowColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0);
	
  [aLayer addSublayer:movielayer];
		
	selected = 0;
	
	[[movielayer movie] play];
	[[movielayer movie] setMuted:YES];
	
	[[movielayer movie] setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute];

}



-(void)setContents:(NSBitmapImageRep*)bitRep mipMap:(NSBitmapImageRep*)mipRep toLayer:(NSString*)name
{
	CALayer * thisLayer = [self layerWithName:name];
	if (thisLayer == nil) return;
	
	NSSize imageSize = [mipRep size];
	CALayer * imagelayer = [[thisLayer sublayers] objectAtIndex:0];

	CGRect f = [imagelayer frame];
	f.size.width = imageSize.width;
	f.size.height = imageSize.height;
	
	CGPoint p = thisLayer.position;
	[thisLayer setFrame:CGRectMake(p.x - 25, p.y - 25, 50, 50)];
	
	[imagelayer setFrame:f];
	imagelayer.position = CGPointMake(25,25);
		
	imagelayer.contents = (id)[mipRep CGImage];
	[imagelayer setValue:[NSNumber numberWithFloat:f.size.width] forKey:@"BBWidth"];
	[imagelayer setValue:bitRep forKey:@"bitRep"];
	[imagelayer setValue:mipRep forKey:@"mipRep"];
	
	[imagelayer setValue:(id)[bitRep CGImage] forKey:@"highRez"];
	
	[imagelayer setValue:(id)[mipRep CGImage] forKey:@"mipmap"];
	
}

-(void)allImagesLoaded:(id)sender
{
	CGFloat o = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBShadowOpacity"];
	if ([[NSUserDefaults standardUserDefaults] boolForKey:@"BBShowShadowsFlag"]) {
		for (CALayer * lyr in [bodyLayer sublayers]) {
			CALayer * photoLayer = [[lyr sublayers] objectAtIndex:0];
			photoLayer.shadowOpacity = o;		
		}
	}
}



-(void)layoutCircle
{
	CGFloat x,y;
	int zPos = 0;
	CGFloat angleIncrement = (2.0 * 3.14159)/(CGFloat)[[bodyLayer sublayers] count];
	CGFloat angle = 0;
	for (CALayer * l in [bodyLayer sublayers]) {
		l.zPosition = zPos++;
		x = 300 * cosf(angle) + NSMidX([self bounds]);
		y = 300 * sinf(angle) + NSMidY([self bounds]);
		l.position = CGPointMake(x, y);
		angle += angleIncrement;
		CATransform3D shmush =  CATransform3DMakeRotation(0.0, 0.0, 0.0, 1.0);
		l.sublayerTransform = CATransform3DScale(shmush, 0.5, 0.5, 1.0); 	
	}
//	CALayer * selectedLayer = [[bodyLayer sublayers] objectAtIndex:selected];
//	selectedLayer.zPosition = ++zPos;
//	[self performSelector:@selector(layoutCircle2) withObject:nil afterDelay:8.0];
}


-(void)layoutCircle2
{
	CGFloat x,y;
	int zPos = 0;
	CGFloat angleIncrement = (2.0 * 3.14159)/(CGFloat)[[bodyLayer sublayers] count];
	CGFloat angle = 0;
	for (CALayer * l in [bodyLayer sublayers]) {
		l.zPosition = zPos++;
		x = 300 * sinf(angle) + NSMidX([self bounds]);
		y = 200 * cosf(angle + 3.14) + NSMidY([self bounds]);
		l.position = CGPointMake(x, y);
		angle += angleIncrement;
		CATransform3D shmush =  CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0);
		l.sublayerTransform = CATransform3DScale(shmush, 0.5, 0.5, 1.0); 	
	}
	//	CALayer * selectedLayer = [[bodyLayer sublayers] objectAtIndex:selected];
	//	selectedLayer.zPosition = ++zPos;
}

-(void)layoutGrid
{
	CGFloat x,y;
	int zPos = 0;
	
	
	for (CALayer * l in [bodyLayer sublayers]) {
		l.zPosition = zPos;
		l.position = CGPointMake(x, y);
		CATransform3D shmush =  CATransform3DMakeRotation(0.0, 0.0, 0.0, 1.0);
		l.sublayerTransform = CATransform3DScale(shmush, 0.5, 0.5, 1.0); 	
	}
	//	CALayer * selectedLayer = [[bodyLayer sublayers] objectAtIndex:selected];
	//	selectedLayer.zPosition = ++zPos;
	[self performSelector:@selector(layoutMess) withObject:nil afterDelay:8.0];
}


-(void)layoutSelected
{
//	int zPos = 0;
//	for (CALayer * l in [bodyLayer sublayers]) {
//		if (l.zPosition > zPos) zPos = l.zPosition;
//	}
	CALayer * selectedLayer = [[bodyLayer sublayers] objectAtIndex:selected];
	selectedLayer.zPosition = ++maxZpos;
}

-(void)otherMouseDown:(NSEvent*)theEvent
{
	[self mouseDown:theEvent];		
}

-(void)blobDownEvent:(NSNotification*)note
{
	[self mouseDown:[note object]];	
}

-(void)mouseDown:(NSEvent*)theEvent
{	
	NSPoint mouseDown = [self convertPoint:[theEvent locationInWindow] toView:self];
	CALayer * hitLayer = [[self layer] hitTest:NSPointToCGPoint(mouseDown)];
	
	if (hitLayer  == coverButtonLayer) {
		// then it is a button
		NSString * path = [[[bodyLayer sublayers] lastObject] name];
		path = [path stringByDeletingLastPathComponent];

		[[NSNotificationCenter defaultCenter] postNotificationName:@"BBShuffleViewsNotification" object:path];	
		return;
	}

	if (hitLayer  == quitButtonLayer) {
		// then it is a button
		
		[[NSNotificationCenter defaultCenter] postNotificationName:@"BBQuitAndLaunch" object:self];	
		return;
	}

	if (hitLayer  == circleLayoutButtonLayer) {
		// then it is a button
		if (circle1) {
			[self layoutCircle];			
		} else {
			[self layoutCircle2];
		}
		circle1 = !circle1;
		return;
	}
	
	if (hitLayer  == lightBoxButtonLayer) {
		// then it is a button
		[self layoutMess];
		return;
	}
	
	hitLayer = [bodyLayer hitTest:NSPointToCGPoint(mouseDown)];
	
	NSInteger selectionIndex = [[bodyLayer sublayers] indexOfObject:hitLayer.superlayer];
	
	if (selectionIndex == NSNotFound) return;
	selected = selectionIndex;
	[self layoutSelected];
}


-(void)blobEventsNotification:(NSNotification*)note 
{
	[super blobEventsNotification:note];
	if (cursors == nil) self.cursors = [NSMutableArray array];
	NSArray * newCursors = [note object];
	if (([cursors count] == 0) && ([newCursors count] > 0)) {
		[self didStartManipulating];
	}
	if (([cursors count] > 0) && ([newCursors count] == 0)) {
		[self didEndManipulating];
	}
	[cursors removeAllObjects];
	[cursors addObjectsFromArray:newCursors];
	[self blobCycleDone];
}

-(void)blobCycleDone
{
	// do the thing
	// find all the htis of the layers and pass on the fun
	for (BBTUIOCursor * cursor in cursors) {
		[self dispatchCursor:cursor];
	}
	
	[CATransaction begin];
	// make sure the dragging happens immediately. we set a specific
	// value here in case we want to it be nearly instant (0.1) later        
	[CATransaction setValue: [NSNumber numberWithBool:0.0]
									 forKey: kCATransactionAnimationDuration];
	
	for (CALayer* l in [bodyLayer sublayers]) {
		[[[l sublayers] objectAtIndex:0] finishCursorCycle];
	}
	
	// now handle my own events
	[gestureDecoder finishCursorCycle];
	[self.layer removeAllAnimations];	
	if (gestureDecoder.eventMask & BBDragEventMask) [self handleDrag];
//	if (gestureDecoder.eventMask & BBRotateEventMask) [self handleRotate];
	//	if (gestureDecoder.eventMask & BBScaleEventMask) [self handleScale];
	
	[CATransaction commit];
	
//	[self setNeedsDisplay:YES];
}

-(void)didStartManipulating
{
//	[CATransaction begin];
	for (CALayer * lyr in [bodyLayer sublayers]) {
		CALayer * photoLayer = [[lyr sublayers] objectAtIndex:0];
		[photoLayer removeAnimationForKey:@"shadowOpacity"];
		photoLayer.shadowOpacity = 0;
	}
//	[CATransaction commit];
}

-(void)didEndManipulating
{
	if (![[NSUserDefaults standardUserDefaults] boolForKey:@"BBShowShadowsFlag"]) return;
	if ([movingParts count] > 0) return;
	
	CGFloat o = [[NSUserDefaults standardUserDefaults] floatForKey:@"BBShadowOpacity"];
	//[CATransaction begin];
	for (CALayer * lyr in [bodyLayer sublayers]) {
		CALayer * photoLayer = [[lyr sublayers] objectAtIndex:0];
		[photoLayer removeAnimationForKey:@"shadowOpacity"];
		photoLayer.shadowOpacity = o;
	}
//	[CATransaction commit];
}

-(void)handleDrag
{
	// now I have 2 matching cursors, it is a drag
	CGPoint dragVector = gestureDecoder.vector;
	//	offset = CGPointMake(offset.x + dragVector.x, offset.y + dragVector.y);
	CATransform3D translate = CATransform3DMakeTranslation(dragVector.x, dragVector.y, 0);	
	bodyLayer.sublayerTransform = CATransform3DConcat(bodyLayer.sublayerTransform, translate);
}


-(void)dispatchCursor:(BBTUIOCursor*)aCursor
{
	// need to scale the 0..1 style TUIO coordinates to our bounds
	// TODO: cache these
	float scaleX = NSWidth([self bounds]);
	float scaleY = NSHeight([self bounds]);
	
	NSPoint loc = [aCursor position];
	
	// flip the y, it is from the top down in TUIO parlance,
	// but our views are bottom up
	loc.y = 1.0 - loc.y;
	
	loc.x *= scaleX;
	loc.y *= scaleY;
	
	aCursor.screenPosition = loc;
	
	CALayer* hitLayer = [bodyLayer hitTest:CGPointMake(loc.x, loc.y)];
	if (hitLayer == bodyLayer) {
		[gestureDecoder addCursor:aCursor];
		return;		
	}
	if ([hitLayer isKindOfClass:[BBMovieLayer class]] || [hitLayer isKindOfClass:[BBPhotoLayer class]]) {
		[[(BBPhotoLayer*)hitLayer gestureDecoder] addCursor:aCursor];
		[self selectLayer:hitLayer.superlayer];
		return;		
	}
	if ([hitLayer.name isEqualToString:@"photoLayer"]) {
		[[[[hitLayer sublayers] objectAtIndex:0] gestureDecoder] addCursor:aCursor];
		[self selectLayer:hitLayer];		
	}
}

-(void)selectLayer:(CALayer*)l1
{
	NSInteger index = [[bodyLayer sublayers] indexOfObject:l1];
	if (index == NSNotFound) return;
	selected = index;
	[self layoutSelected];
}


- (void) dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	for (NSString * keyPath in [self observingKeyPaths]) {
		[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:keyPath];
	}
	[super dealloc];
}


@end
